home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / misc_pto / mcbtrc2 / trace.c < prev    next >
C/C++ Source or Header  |  1989-11-26  |  11KB  |  385 lines

  1. /*
  2.  * Antoun Kanawati
  3.  * November 25, 1989
  4.  *
  5.  * Another DOS hacking program.
  6.  *
  7.  *                    A program to trace DOS's MCB chain.
  8.  *
  9.  * A Memory Control Block (MCB) is an undocumented DOS data structure built as 
  10.  * follows:
  11.  *    size      = 16 bytes.
  12.  *    offset    contents
  13.  *      0       byte   'M' ==> middle of chain.
  14.  *                     'Z' ==> end of chain.
  15.  *      1       word    0  ==> Free.
  16.  *                   != 0  ==> Segment address of owner (PSP address).
  17.  *      3       word       ==> Size of block.
  18.  *      4       11 bytes   ==> reserved or unused.
  19.  *
  20.  * For each block that DOS allocates thru function 0x48, one of these blocks
  21.  * is created, an it is just before the block whose address is returned.
  22.  * (This is true even for blocks of size 0.)
  23.  *
  24.  * Furthermore, all MCB's are aligned on paragraph boundaries.
  25.  *
  26.  * The program first sets DOS's strategy to ``first fit'', and then requests a
  27.  * block of size 0 from DOS.  This insures that we get the lowest possible
  28.  * paragraph address.
  29.  *
  30.  * Then, the guessing starts:
  31.  *   We look backwards for an MCB that lies before the one just allocated, 
  32.  *   and we keep looking backwards until the end of chain (avoiding the 
  33.  *   BIOS data area).
  34.  *   Once we have an estimate of the start point, it is a simple matter to 
  35.  *   trace the chain forward.
  36.  *
  37.  * The program name is printed by looking in the owner's PSP, and scanning the
  38.  * owner's environment block (if it is still attached to the program).
  39.  *
  40.  * For details on how the environment is located, look at ADVANCED MS-DOS, or
  41.  * the MS-DOS encyclopedia.
  42.  *
  43.  * The MCB structure is sketched in a few non-official references.  Official 
  44.  * MS-DOS references (such as the two above) barely mention it.
  45.  * None of the references I have seen mention how to find the starting MCB, 
  46.  * so I had to write an educated guessing routine to do it.
  47.  *
  48.  * There might be a better method to find the starting MCB than the one 
  49.  * presented here.  Unfortunately, I am not aware of it.
  50.  *
  51.  * A warning:
  52.  *  This program sets DOS's allocation strategy to ``first fit''.  Since there
  53.  *  are two other strategies, this means we could be changing something.  If 
  54.  *  you intend to use the techniques presented here in an environment where
  55.  *  allocation strategies matter, you should restore the strategy to its 
  56.  *  original value before terminating the program.
  57.  *
  58.  * A comment on the use of the program:
  59.  *  The casual user and the high level programmer will find this stuff of no 
  60.  *  use.  This is intended for those who like to know what happens for the 
  61.  *  sake of knowing what happens (i.e.: for the fun of it).  There are 
  62.  *  programs of a higher quality output and usefulness available to list all
  63.  *  your memory resident programs, the interrupts they use, etc....
  64.  *  These other niceties are trivial problems once we have traced the chain:
  65.  *  sort the blocks by segment address, and scan the vector table checking the
  66.  *  segment addresses against the owners of MCB's.
  67.  *
  68.  * Disclaimer:
  69.  *  You may use this program at your own risk.  The author is not responsible 
  70.  *  in any manner for damages incurred by the use of the program, nor for
  71.  *  benefits gained from the source code.  That is: feel free to copy, modify, 
  72.  *  and distribute the source code, but do not use it blindly -- This is 
  73.  *  intended for competent programmers with substantial DOS knowledge.  And 
  74.  *  please keep the source code of this program with to its binaries -- The
  75.  *  executable is quite worthless without the source code.
  76.  *
  77.  * A final hacker's note:
  78.  *  You will notice on the output a free block of size zero.  This is the 
  79.  *  block used to start the search backwards for the starting MCB.  You will
  80.  *  also find two blocks bearing this program's name: one is for the 
  81.  *  environment, and the other is for the program.
  82.  *
  83.  *  You need not worry about fragmentation: DOS compacts free memory whenever
  84.  *  necessary, and you can see that by modifying this program to trace the 
  85.  *  allocation chain between allocations and releases of blocks of various 
  86.  *  sizes.
  87.  *
  88.  *  And if you are interested in seeing how allocation strategies affect DOS's
  89.  *  allocation behavior, then you can use parts of this program to trace the
  90.  *  allocation chain between allocations and releases to see where new data is
  91.  *  allocated.
  92.  *
  93.  *  Interesting experiments:
  94.  *    trace
  95.  *    command/c trace
  96.  *    command/c command/c trace
  97.  *
  98.  */
  99. #include <stdio.h>
  100.  
  101. #if sizeof(char *) != 4
  102.   /*
  103.    * The program must operate with 32-bit pointers.
  104.    *
  105.    */
  106. main()
  107. {
  108.   printf("This program must be compiled in a large data pointer model.\n");
  109.   exit(0);
  110. }
  111. #else
  112.  
  113.    /* Going from segment addresses to normalized pointer values. */
  114. #define seg_to_charp(x) ((char *) (((long) (x)) << 16))
  115.  
  116.    /* MCB access protocols: */
  117. #define MCB_owner(ptr)       (*((unsigned *) (((char *) (ptr))+1)))
  118. #define MCB_size(ptr)        (*((unsigned *) (((char *) (ptr))+3)))
  119.  
  120. #define BIOS_END        0x800
  121.  
  122.  
  123. /*
  124.  * DOS call to set first-fit strategy.
  125.  *
  126.  */
  127. void first_fit()
  128. {
  129.   asm mov ah, 0x58;
  130.   asm mov al, 0x01;
  131.   asm mov bx, 0x00;
  132.   asm int 0x21;
  133. }
  134.  
  135. /*
  136.  * DOS call to get a block.
  137.  *
  138.  */
  139. unsigned get_block(unsigned n)
  140. {
  141.   asm mov ah, 0x48;
  142.   asm mov bx, n;
  143.   asm int 0x21;
  144.   asm jc  err;
  145.   asm jmp ok;
  146. err:
  147.   asm xor ax,ax;
  148. ok:
  149.   return _AX;
  150. }
  151.  
  152. /*
  153.  * DOS call to release a block gotten with the above procedure.
  154.  *
  155.  */
  156. void release_block(unsigned n)
  157. {
  158.   unsigned err_code;
  159.  
  160.   asm push es;
  161.   asm mov  ah, 0x49;
  162.   asm mov  es, n;
  163.   asm int  0x21;
  164.   asm pop  es;
  165.   asm jc   err;
  166.   asm jmp  done;
  167.  
  168.   err_code = _AX;
  169.  
  170. err:
  171.   printf("Error code = %04X\n", err_code);
  172.  
  173. done:
  174.   return;
  175. }
  176.  
  177.  
  178. /*
  179.  * Environment scanner:
  180.  *
  181.  *   Given the PSP of the owner checks:
  182.  *      1- PSP starts with a INT 20H instruction.
  183.  *      2- Retrieves the environment block address, and checks that it is
  184.  *         attached to the program.
  185.  *   Then, the evironment is scanned for the program name, which is printed.
  186.  *
  187.  */
  188. void do_env(unsigned psp)
  189. {
  190.   char     *envp;
  191.   char     *psp_ptr;
  192.   unsigned envseg;
  193.   unsigned envsize;
  194.  
  195.   psp_ptr = seg_to_charp(psp);
  196.  
  197.     /* If the PSP does not start with INT 20H, return. */
  198.   if ((*((unsigned *) psp_ptr)) != 0x20CD) /* INT 20H */
  199.     {
  200.       printf("\n");
  201.       return;
  202.     }
  203.  
  204.     /* Look at the MCB of environment block : */
  205.   envseg = *((unsigned *) (psp_ptr + 0x2C));
  206.   envp   = seg_to_charp(envseg-1);
  207.  
  208.  
  209.     /* The MCB must be an M-type, owned by PSP that was used to reach it, */
  210.     /* and must have a non-zero size.                                     */
  211.     /* If any of these conditions fails, we leave.                        */
  212.   if ( *envp != 'M' || MCB_owner(envp) != psp || MCB_size(envp) == 0)
  213.     {
  214.       printf("\n");
  215.       return;
  216.     }
  217.  
  218.  
  219.   envsize = MCB_size(envp) << 4; /* paragraphs --> bytes. */
  220.  
  221.     /* Now, we skip the environment strings, which leaves us at the program */
  222.     /* name.                                                                */
  223.   envp = seg_to_charp(envseg);
  224.  
  225.   while (*envp && envsize)
  226.     while (*envp++ && envsize--)
  227.       ;
  228.  
  229.   envp += 3;
  230.  
  231.     /* If the whole environment was skipped, we print nothing. */
  232.   if (envsize)
  233.     printf("%s\n", envp);
  234.   else
  235.     printf("\n");
  236.  
  237. }
  238.  
  239.  
  240.  
  241. /*
  242.  * Prints contents of MCB:
  243.  *   1- Check for valid format.
  244.  *   2- Pick up the data on size an owner.
  245.  *   3- Print.
  246.  *   4- Try to find an owner's name.
  247.  *
  248.  */
  249. void dump_mcb(unsigned seg_ref)
  250. {
  251.   char      *p;
  252.   unsigned  owner;
  253.   unsigned  size;
  254.  
  255.   p = seg_to_charp(seg_ref);
  256.  
  257.   if (*p != 'M' && *p != 'Z')
  258.     {
  259.       printf("Bad MCB, first byte == %02X\n", (unsigned char) (*p));
  260.       return;
  261.     }
  262.  
  263.   owner = MCB_owner(p);
  264.   size  = MCB_size(p);
  265.  
  266.   printf("%04X  %02X  %04X %04X ",
  267.           seg_ref,
  268.           (unsigned char) (*p),
  269.           owner,
  270.           size);
  271.  
  272.   if (*p == 'Z')
  273.     printf("End of chain.\n");
  274.   else if (owner == 0)
  275.     printf("Free fragment.\n");
  276.   else
  277.     do_env(owner);
  278. }
  279.  
  280.  
  281. /*
  282.  * Forward tracing in the allocation chain.
  283.  *
  284.  * Note that we assume that we have a proper MCB address at start.
  285.  *
  286.  */
  287. void trace_chain(unsigned mcb_seg)
  288. {
  289.   unsigned seg;
  290.   char *p;
  291.  
  292.   seg = mcb_seg;
  293.  
  294.   printf("Addr LBL  OWNR SIZE\n");
  295.   printf("---- ---- ---- ----\n");
  296.  
  297.  
  298.   for(;;)
  299.   {
  300.     dump_mcb(seg);          /* Print the current MCB. */
  301.  
  302.     p = seg_to_charp(seg);
  303.  
  304.     if (*p == 'Z') break;   /* End of chain ? */
  305.  
  306.     if (*p != 'M')          /* Check for errors. */
  307.       {
  308.         printf("Unknown designator : %02X\n", (unsigned char) *p);
  309.         break;
  310.       }
  311.  
  312.     seg = seg + MCB_size(p) + 1;  /* Move to next MCB */
  313.   }
  314.  
  315.  
  316.   printf("\n");  /* Finish table. */
  317. }
  318.  
  319.  
  320. /*
  321.  * Guessing algorithm: This produces an MCB address for what might be the
  322.  * first MCB in the chain using the following criteria:
  323.  *   1- The MCB in question must lead to the current MCB.
  324.  *   2- It must be located above the BIOS data area.
  325.  *   3- It is free, or owned by a process whose PSP is above paragraph address
  326.  *      0x800 (above the BIOS data area).
  327.  *
  328.  * We start with an initial address (i-1), and keep trying backwards until 
  329.  * we hit BIOS data area.  The last guess that passed the test is taken as
  330.  * the start of the chain.
  331.  *
  332.  * Note that this is not 100% guaranteed to be correct, but the chances of
  333.  * finding a bogus MCB are low enough to make it usable.
  334.  *
  335.  */
  336. unsigned find_start(unsigned i)
  337. {
  338.   unsigned a, b;
  339.   char     *p;
  340.  
  341.   for(a = i, b = i-1; b >= BIOS_END; )
  342.     {
  343.       p = seg_to_charp(b);
  344.  
  345.       if (*p == 'M' && MCB_size(p) + b + 1 == a &&
  346.           (MCB_owner(p) >= BIOS_END || MCB_owner(p) == 0))
  347.         a = b;
  348.  
  349.       b--;
  350.     }
  351.  
  352.   return a;
  353. }
  354.  
  355.  
  356. /*
  357.  * And finally, the main() procedure.
  358.  *
  359.  */
  360. main()
  361. {
  362.   unsigned m;
  363.   unsigned start;
  364.  
  365.   first_fit();         /* set strategy to start at lowest address. */
  366.  
  367.   m = get_block(0);    /* Get lowest free address. */
  368.  
  369.   if (m == 0)
  370.     {
  371.       printf("Allocation failed.\n");
  372.       exit(0);
  373.     }
  374.  
  375.   start = find_start(m-1);   /* Find a start MCB. */
  376.  
  377.   release_block(m);          /* get rid of the MCB used to start. */
  378.  
  379.   trace_chain(start);
  380.  
  381.   exit(0);                   /* A clean exit. */
  382. }
  383. #endif
  384.  
  385.